<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Steering Behaviours: PATH FOLLOW</title>

    <script src="../../libs/dat.gui.min.js"></script>
    <script src="../../libs/stats.min.js"></script>
    <script src="../../libs/three.min.js"></script>
    <script src="../../js/threejs/sea3d/SEA3D.js"></script>
    <script src="../../js/threejs/sea3d/SEA3DLZMA.js"></script>
    <script src="../../js/threejs/sea3d/SEA3DLoader.js"></script>
    <script src="../../js/threejs/controls/OrbitControls.js"></script>
    <script src="../../js/ThreeSteer.js"></script>
    <link rel="stylesheet" type="text/css" href="../css/main.css"/>
    <link rel="stylesheet" type="text/css" href="../css/gui.css"/>
</head>
<script>
    var container;
    var camera;

    var scene, renderer;
    var clock;
    var controls;
    var stats;

    var boundaries;
    var entities;
    var params;
    var mesh;

    var path;
    var arrows, flags;

    var characters=[
        {index:0, name:"white", url:"models/avatar_white.tjs.sea"},
        {index:1, name:"red",   url:"models/avatar_red.tjs.sea"},
        {index:2, name:"black", url:"models/avatar_black.tjs.sea"}
    ];

    var animations=[
        {index:0, name:"idle", url:"models/animations/idle.tjs.sea"},
        {index:1, name:"walk", url:"models/animations/walk.tjs.sea"},
        {index:2, name:"jog", url:"models/animations/jog.tjs.sea"},
        {index:3, name:"run",  url:"models/animations/run.tjs.sea"}
    ];

    function onClick(event) {
        if (event.altKey) {
            var mouse3D = new THREE.Vector3(( event.clientX / window.innerWidth ) * 2 - 1, -( event.clientY / window.innerHeight ) * 2 + 1, 0);
            var raycaster = new THREE.Raycaster();
            raycaster.setFromCamera(mouse3D, camera);
            var intersects = raycaster.intersectObjects(scene.children);
            if (intersects.length > 0) {
                var point=new THREE.Vector3(intersects[0].point.x, entities[0].position.y, intersects[0].point.z)
                addFlag(point)
                path.push(point)

            }
        }
        else if(event.ctrlKey)
        {
            reset();
        }
    }


    function loadModels() {
        loadModel(0)
    }

    function loadModel(index) {
        var loader = new THREE.SEA3D();
        loader.onProgress = function (e) {
            document.getElementById("loading-msg").innerHTML = "Loading model " + parseInt(index + 1) + " of " + characters.length //+ " ( " + parseInt(e.progress * 100) + "% )";
        };

        loader.onError = function (e) {
            index++;
            loadModel(index);
        };

        loader.onComplete = function (e) {

            characters[index].mesh=loader.meshes[0]
            characters[index].mesh.rotateY(Math.PI)

            if (index < characters.length - 1) {
                index++;
                loadModel(index);
            }
            else {
              loadAnimations()
            }
        }
        loader.load(characters[index].url);
    }


    function loadAnimation(index) {
        var loader = new THREE.SEA3D();
        loader.onProgress = function (e) {
            document.getElementById("loading-msg").innerHTML = "Loading animation " + parseInt(index + 1) + " of " + animations.length //+ " ( " + parseInt(e.progress * 100) + "% )";
        };

        loader.onError = function (e) {
            index++;
            loadAnimation(index);
        };

        loader.onComplete = function (e) {

            animations[index].data=loader.meshes[0].animations[0]

            if (index < animations.length - 1) {
                index++;
                loadAnimation(index);
            }
            else {
                var el = document.getElementById("loading-msg")
                el.parentNode.removeChild(el);
                init('container');
            }
        }
        loader.load(animations[index].url);
    }

    function loadAnimations() {

        loadAnimation(0)
    }

    function init(element) {
        container = document.getElementById(element);
        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 50000);
        scene = new THREE.Scene();

        renderer = new THREE.WebGLRenderer({alpha: true});
        renderer.setClearColor(0x333333, 0);
        renderer.shadowMap.enabled=true;
        renderer.shadowMap.type = THREE.PCFSoftShadowMap;
        renderer.gammaInput = true;
        renderer.gammaOutput = true;

        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.domElement.style.position = 'absolute';
        renderer.domElement.style.top = 0;
        container.appendChild(renderer.domElement);

        camera.position.set(0, 10000, 10000);
        camera.lookAt(new THREE.Vector3(0, 0, 0));

        controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.maxPolarAngle = Math.PI * 0.5;
        controls.minDistance = 100;
        controls.maxDistance = 30000;

        stats = new Stats();
        stats.domElement.style.position = 'absolute';
        stats.domElement.style.bottom = '0px';
        stats.domElement.style.left = '180px'
        stats.domElement.style.zIndex = 100;
        container.appendChild(stats.domElement);

        clock = new THREE.Clock();


        //Lights
        var spotLight = new THREE.SpotLight( 0xffffff, 5.0 );
        spotLight.position.set( 0, 5000, 0 );
        spotLight.castShadow = true;
        spotLight.shadow.mapSize.width = 1024;
        spotLight.shadow.mapSize.height = 1024;
        spotLight.angle = 1;
        spotLight.penumbra = 0.5;
        spotLight.decay = 2;
        spotLight.distance = 10000;


        var ambient = new THREE.AmbientLight( 0xFFFFFF );
        ambient.intensity=0.25
        scene.add( spotLight );
        scene.add( ambient );

        // Floor
        var floorGeometry = new THREE.CubeGeometry(10000, 10000, 1, 32);
        var floorMaterial = new THREE.MeshPhongMaterial({color: 0x4DA6FF});
        var floor = new THREE.Mesh(floorGeometry, floorMaterial);
        floor.receiveShadow=true;
        floor.rotation.x = -Math.PI * .5;
        scene.add(floor);

        path=[]
        flags=[]
        arrows=[]

        params={maxSpeed:6, maxForce:3,  numEntities:5, thresholdRadius:10, loop:true}



        // Entities
        entities=[]
        for(var i=0;i<params.numEntities;i++)
        {
            var randomChar=Math.floor(Math.random() * ((2-0)+1) + 0);
            var clone = new THREE.SEA3D.SkinnedMesh(characters[randomChar].mesh.geometry, characters[randomChar].mesh.material)
            for(var j=0;j<animations.length;j++)
            {
                clone.animations.push(animations[j].data);
                clone.clips.push(animations[j].data);
                clone.animationsData[j] = animations[j].data;
            }

            clone.rotateY(Math.PI)
            clone.scale.set(1,1,1)
            clone.castShadow=true;
            var entity=new SteeringEntity(clone);
            entity.position.set(Math.random() * (5000 - (-5000)) + (-5000) ,0,Math.random() * (5000 - (-5000)) + (-5000));
            entities.push(entity);
            scene.add(entity);

        }

        //Plane boundaries (do not cross)
        boundaries = new THREE.Box3(new THREE.Vector3(-5000, 0, -5000), new THREE.Vector3(5000, 0, 5000));

        //Plane boundaries (do not cross)
        boundaries = new THREE.Box3(new THREE.Vector3(-5000, 0, -5000), new THREE.Vector3(5000, 0, 5000));

        //Gui
        var gui = new dat.GUI();

        var quantity = gui.add(params, 'numEntities', 1, 100).name("Num Of Entities");
        quantity.onFinishChange(function(value) {
            for(var i=0;i<entities.length;i++)
            {
                entities[i].mesh.traverse(function(obj) {
                    scene.remove(obj);
                    if(obj.geometry)
                        obj.geometry.dispose();
                    if(obj.material)
                        obj.material.dispose();
                    if(obj.mesh)
                        obj.mesh.dispose();
                    if(obj.texture)
                        obj.texture.dispose();

                });
                scene.remove(entities[i])

            }

            entities=[]

            for(var i=0;i<value;i++)
            {

                var randomChar=Math.floor(Math.random() * ((2-0)+1) + 0);
                var clone = new THREE.SEA3D.SkinnedMesh(characters[randomChar].mesh.geometry, characters[randomChar].mesh.material)
                for(var j=0;j<animations.length;j++)
                {
                    clone.animations.push(animations[j].data);
                    clone.clips.push(animations[j].data);
                    clone.animationsData[j] = animations[j].data;
                }

                clone.rotateY(Math.PI)
                clone.scale.set(1,1,1)
                clone.castShadow=true;

                var entity=new SteeringEntity(clone)
                entity.position.set(Math.random() * (5000 - (-5000)) + (-5000) ,0,Math.random() * (5000 - (-5000)) + (-5000));
                entities.push(entity);
                scene.add(entity);

            }
        });


        gui.add(params, 'maxSpeed', 2, 20).name('Max Speed').step(1);
        gui.add(params, 'maxForce', 1, 20).name('Max Force').step(1);
        gui.add(params, 'thresholdRadius', 10, 500).name('Threshold Radius').step(1);
        gui.add(params, 'loop').name('Loop');

        window.addEventListener('resize', onWindowResize, false);
        document.addEventListener( 'mousedown', onClick, true );
        animate();
    }

    function reset()
    {
        var i;
        for(i=0;i<flags.length;i++)
            scene.remove(flags[i])
        for(i=0;i<arrows.length;i++)
            scene.remove(arrows[i])
        path=[]
        flags=[]
        arrows=[];
        for(var i=0;i<entities.length;i++)
        {
            entities[i].pathIndex=0
        }

    }

    function addFlag(position)
    {
        addArrow(position)
        var flagGeometry=new THREE.CylinderGeometry(10, 10, 300, 32);
        var flagMaterial=new THREE.MeshPhongMaterial({color: 0x002DB2});
        var flag=new THREE.Mesh( flagGeometry, flagMaterial );
        flag.castShadow=true;
        flag.position.set(position.x,150,position.z);
        scene.add(flag);
        flags.push(flag)

    }

    function addArrow(position)
    {
        if(path.length>0)
        {
            var start=new THREE.Vector3(path[path.length-1].x, path[path.length-1].y, path[path.length-1].z);
            var distance=path[path.length-1].distanceTo(position)
            var direction=position.clone().sub(path[path.length-1]).normalize()
            var arrow=new THREE.ArrowHelper(direction, start, distance, 0x002DB2, distance-1, 100)
            arrow.cone.material.transparent=true;
            arrow.cone.material.opacity=0.5;
            scene.add(arrow)
            arrows.push(arrow)
        }
    }



    function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    }

    function animate() {

        requestAnimationFrame(animate);


        var delta = clock.getDelta();
        THREE.SEA3D.AnimationHandler.update(delta);
        controls.update();


        for(var i=0;i<entities.length;i++)
        {
            entities[i].maxSpeed=params.maxSpeed;
            entities[i].maxForce=params.maxForce;

            entities[i].setAnimation()

            if(path.length>0)
            {
                entities[i].followPath(path, params.loop, params.thresholdRadius);
                entities[i].lookWhereGoing(true)
            }

            else
            {
                entities[i].idle()
            }

            entities[i].update();

        }

        renderer.render(scene, camera);
        stats.update();

    }
    Entity.prototype.setAnimation=function()
    {
        if(this.velocity.length()<=1)
            this.mesh.play(1, 0.5) ;//Idle

        else if(this.velocity.length()>1 && this.velocity.length()<=8)
            this.mesh.play(2, 0.5) //Walk

        else if(this.velocity.length()>8 && this.velocity.length()<=12)
            this.mesh.play(3, 0.5) //Jog

        else if(this.velocity.length()>12)
            this.mesh.play(4, 0.5)//Run
    }
</script>

<body onload="loadModels()">
<div id="container"></div>
<div id="msg"><span style="color:#BFFF00; font-weight: bold">PATH FOLLOW:</span><br>ALT + Click to add points to path.<br>CTRL + Click to reset.</div>

<div id="loading-msg"></div>
<a href="https://github.com/erosmarcon/three-steer/blob/master/examples/animated/PathFollow.html" target="_blank">
    <div class="view-source">View source</div>
</a>
</body>

</html>